﻿/***
 * TODO
 * ````
 * - Chuyển đổi tệp thành định dạng DOCX nếu nó chưa phải là tệp DOCX.
 * 
 * 
 * HISTORY
 * ``````` *  
 * 2014/02/24-10:59:53.66
 * - Hoàn thành hàm chuyển một tệp đầu vào thành bộ kết quả (tệp đầu ra, chi tiết kết quả)
 *      + nếu thành công: tệp kết quả, thời gian thực hiện, danh sách cảnh báo nếu có.
 *      + nếu không thành công: thông tin gây ra lỗi.
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Novacode;
using QuanLyNganHangDe.DAL;

namespace MarkDocuments
{
    public partial class FormTest : Form
    {
        public FormTest()
        {
            InitializeComponent();
        }

        private void InsertTextBeforeFirstVisibleChar(ref Paragraph par, string text)
        {
            var parContent = par.Text;
            if (parContent == "")
            {
                par.InsertText(text);
            }
            else
            {
                var idx = 0;
                while (true)
                {
                    if (idx > parContent.Length - 1)
                        break;
                    var ch = parContent[idx];
                    if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
                        idx++;
                    else
                        break;
                }
                if (idx > parContent.Length - 1)
                    idx = parContent.Length - 1;
                par.InsertText(idx, text);
            }

            //System.Diagnostics.Debug.WriteLine(par.Text);
        }

        private string ExportTaggedDocumentOf(string stFilename)
        {
            var stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();
            var stFilename2 = System.IO.Path.Combine(System.IO.Path.GetTempPath(),
                System.Guid.NewGuid().ToString() + ".docx");
            var option = Program.TagOptions;
            string PHAN_DAP_AN = "II. PHẦN ĐÁP ÁN";
            using (var document = DocX.Load(stFilename))
            {
                #region var lstParCauHoi = danh sách các par câu hỏi;
                var lstParCauHoi = new List<Paragraph>();
                var idxEndOfCauHoiPar = -1;
                for (int i = 0; i < document.Paragraphs.Count; i++)
                {
                    var par = document.Paragraphs[i];
                    var parContent = par.Text.Trim().ToUpper();

                    if (parContent != PHAN_DAP_AN)
                    {
                        lstParCauHoi.Add(par);
                    }
                    else
                    {
                        idxEndOfCauHoiPar = i;
                        break;
                    }
                }
                #endregion

                #region var lstParDapAn = danh sách các par đáp án; bổ sung [lanw_start] ở đầu phần đáp án
                var lstParDapAn = new List<Paragraph>();
                for (int i = idxEndOfCauHoiPar; i < document.Paragraphs.Count; i++)
                {
                    lstParDapAn.Add(document.Paragraphs[i]);
                }
                // Loại bỏ các đoạn rỗng (nếu có) ở phần cuối cùng
                var idxLast = lstParDapAn.Count - 1;
                while (true)
                {
                    var par = lstParDapAn[idxLast];
                    var parContent = par.Text.Trim();
                    if (parContent == "")
                    {
                        lstParDapAn.RemoveAt(idxLast);
                        idxLast = lstParDapAn.Count - 1;
                        if (idxLast < 0)
                            break;
                    }
                    else
                        break;
                }
                #endregion

                #region Bổ sung các thẻ cho các khối câu hỏi, câu hỏi
                var laKhoiCauHoiDauTien = true;
                var laCauHoiDauTienCuaKhoi = true;
                for (int i = 0; i < lstParCauHoi.Count; i++)
                {
                    var par = lstParCauHoi[i];
                    var text = par.Text;
                    if (text.StartsWith("Khối "))
                    {
                        #region Bắt đầu 1 khối..
                        laCauHoiDauTienCuaKhoi = true;
                        if (laKhoiCauHoiDauTien)
                        {
                            #region là khối câu hỏi đầu tiên => [lq_start]\[part_start]
                            par.InsertText(0, option.PhanCauHoi_Begin + "\r\n" + option.KhoiCauHoi_Begin);
                            laKhoiCauHoiDauTien = false;
                            #endregion
                        }
                        else
                        {
                            #region là khối câu hỏi thứ 2, 3, ... => [question_end][part_end]\[part_start]
                            par.InsertText(0, option.CauHoi_End + "\r\n" + option.KhoiCauHoi_End + "\r\n" + option.KhoiCauHoi_Begin);
                            #endregion
                        }
                        #endregion
                    }
                    else if (text.StartsWith("Câu "))
                    {
                        #region Bắt đầu 1 câu hỏi
                        if (laCauHoiDauTienCuaKhoi)
                        {
                            #region câu hỏi đầu tiên của khối => [question_start]
                            par.InsertText(0, option.CauHoi_Begin);
                            laCauHoiDauTienCuaKhoi = false;
                            #endregion
                        }
                        else
                        {
                            #region câu hỏi thứ 2, 3, ... của khối => [question_end]\[question_start]
                            par.InsertText(0, option.CauHoi_End + "\r\n" + option.CauHoi_Begin);
                            #endregion
                        }
                        #endregion
                    }
                }
                #endregion

                #region Đóng câu hỏi cuối cùng, đóng khối câu hỏi cuối cùng và đóng phần câu hỏi [question_end]\[part_end]\[lq_end]
                var lastQuestionPar = lstParCauHoi[lstParCauHoi.Count - 1];
                lastQuestionPar.AppendLine(option.CauHoi_End);
                lastQuestionPar.AppendLine(option.KhoiCauHoi_End);
                lastQuestionPar.AppendLine(option.PhanCauHoi_End);
                #endregion

                #region Bổ sung các thẻ cho các khối trả lời, câu trả lời
                var laDapAnDauTienCuaKhoi = true;
                var laKhoiDapAnDauTien = true;
                int soLuongDapAn = 0;
                for (int i = 0; i < lstParDapAn.Count; i++)
                {
                    var par = lstParDapAn[i];
                    var parcontent = par.Text.Trim().ToUpper();
                    if (parcontent == PHAN_DAP_AN)
                    {
                        #region Bắt đầu phần đáp án => Bổ sung [lanw_start] truước II. PHẦN ĐÁP ÁN
                        par.InsertText(0, option.PhanDapAn_Begin);
                        #endregion
                    }
                    else if (parcontent.StartsWith("KHỐI "))
                    {
                        laDapAnDauTienCuaKhoi = true;
                        #region Bắt đầu khối đáp án => bổ sung kết thúc đáp án ([anw_end]) cuối cùng của khối trước đó (nếu có)
                        if (!laKhoiDapAnDauTien)
                        {
                            var idxPrevAnswer = i - 3;
                            if (idxPrevAnswer >= 0 && soLuongDapAn > 0)
                            {
                                var parPrevAnswer = lstParDapAn[idxPrevAnswer];
                                parPrevAnswer.Append(option.DapAn_End);
                            }
                        }
                        laKhoiDapAnDauTien = false;
                        #endregion
                    }
                    else if (parcontent.StartsWith("CÂU "))
                    {
                        #region Bắt đầu 1 đáp án cho câu hỏi => [anw_start]
                        //try
                        {
                            //InsertTextBeforeFirstVisibleChar(ref par, option.DapAn_Begin);
                            //par.InsertText(0, option.DapAn_Begin); // gặp lỗi!!!
                            //par.ReplaceFirstText("Câu ", option.DapAn_Begin + "Câu ", false, System.Text.RegularExpressions.RegexOptions.None);
                            par.InsertTextBeforeFirstVisibleChar(option.DapAn_Begin);
                        }
                        //catch { }
                        #endregion

                        soLuongDapAn++;

                        if (!laDapAnDauTienCuaKhoi)
                        {
                            #region Không phải là đáp án đầu tiên => Thêm phần kết thúc đáp án [anw_end] của câu trước đó
                            var idxPrevParagraph = i - 1;
                            if (0 <= idxPrevParagraph && idxPrevParagraph <= lstParDapAn.Count - 1)
                            {
                                var prevPara = lstParDapAn[idxPrevParagraph];
                                prevPara.Append(option.DapAn_End);
                            }
                            #endregion
                        }
                        laDapAnDauTienCuaKhoi = false;
                    }
                }
                #endregion

                #region Đóng đáp án cuối cùng => [anw_end]
                // Ô tương ứng điểm số của đáp án cuối là par cách par cuối cùng 2 phần tử
                var idxLastAnswerPar = lstParDapAn.Count - 3;
                if (0 <= idxLastAnswerPar && idxLastAnswerPar <= lstParDapAn.Count - 1)
                {
                    var lastAnswerPar = lstParDapAn[idxLastAnswerPar];
                    lastAnswerPar.Append(option.DapAn_End);
                }
                #endregion

                #region Đóng phần đáp [lanw_end]
                document.Tables[document.Tables.Count - 1].InsertParagraphAfterSelf(option.PhanDapAn_End);
                #endregion

                document.SaveAs(stFilename2);
            }
            stopwatch.Stop();
            var stMessage = string.Format("Đã thêm tag trong {0}ms", stopwatch.ElapsedMilliseconds);
            MessageBox.Show(stMessage);

            return stFilename2;
        }

        public class TaggerInput
        {
            public string InputFileName { get; private set; }
            public string OutputFileName { get; private set; }
            public TaggerInput(string inputFileName, string outputFileName)
            {
                InputFileName = inputFileName;
                OutputFileName = outputFileName;
            }
        }

        public class TaggerOutput
        {
            public Exception Error { get; private set; }
            public long TotalWorkingTime { get; private set; }
            public string TotalErrors { get; private set; }

            public TaggerOutput(long totalWorkingTime, string totalErrors)
            {
                this.Error = null;
                this.TotalWorkingTime = totalWorkingTime;
                this.TotalErrors = totalErrors;
            }

            public TaggerOutput(Exception exc)
            {
                this.Error = exc;
            }
        }

        private static string SEP = " ( \r\n\t";
        private static string KHOI = "KHỐI ";
        private static string KHOI_CHARS = "KHỐI 0123456789.:";
        private static string CAU = "CÂU ";
        private static string CAU_CHARS = "CÂU 0123456789.:";
        private static string PHAN_DAP_AN = "II. PHẦN ĐÁP ÁN";
        private static string NEWLINE = " \n";

        /// <summary>
        /// đoạn văn bản có phải bắt đầu 1 khối?
        /// </summary>
        /// <param name="stText">chuỗi đã được in hoa, xoá các ký tự trắng thừa (\r \n \space) ở đầu</param>
        /// <returns></returns>
        private static bool laBatDauKhoi(string stText)
        {
            stText = stText.Trim('\t', ' ');
            if (stText.StartsWith(KHOI) == false)
                return false;
            else
            {
                var idxNextSpace = -1;
                for (int j = KHOI.Length - 1; j < stText.Length; j++)
                    if (SEP.Contains(stText[j]))
                    {
                        idxNextSpace = j;
                        break;
                    }
                if (idxNextSpace < 0)
                    return true;
                else
                {
                    stText = stText.Substring(0, idxNextSpace);
                    for (int j = 0; j < stText.Length; j++)
                        if (KHOI_CHARS.Contains(stText[j]) == false)
                            return false;
                    return true;
                }
            }
        }

        /// <summary>
        /// đoạn văn bản có phải bắt đầu 1 câu?
        /// </summary>
        /// <param name="stText">chuỗi đã được in hoa, xoá các ký tự trắng thừa (\r \n \space) ở đầu</param>
        /// <returns></returns>
        private static bool laBatDauCau(string stText)
        {
            stText = stText.Trim('\t', ' ');
            if (stText.StartsWith(CAU) == false)
                return false;
            else
            {
                var idxNextSpace = -1;
                for (int j = CAU.Length - 1; j < stText.Length; j++)
                    if (SEP.Contains(stText[j]))
                    {
                        idxNextSpace = j;
                        break;
                    }
                if (idxNextSpace < 0)
                    return true;
                else
                {
                    stText = stText.Substring(0, idxNextSpace);
                    for (int j = 0; j < stText.Length; j++)
                        if (CAU_CHARS.Contains(stText[j]) == false)
                            return false;
                    return true;
                }
            }
        }

        public static System.Threading.SynchronizationContext MainContext = System.Threading.SynchronizationContext.Current;

        private void btnProcess_Click(object sender, EventArgs e)
        {
            var bw = new BackgroundWorker();
            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += (oSender, oE) =>
            {
                #region MyRegion
                var lstExc = new List<Exception>();
                var oTaggerInput = (oE.Argument as TaggerInput);
                var worker = (oSender as BackgroundWorker);
                var stopwatch = new System.Diagnostics.Stopwatch();
                stopwatch.Start();
                var stFilename = oTaggerInput.InputFileName;
                if (System.IO.File.Exists(stFilename) == false)
                    throw new Exception("Tệp đầu vào không tồn tại");
                var stFilename2 = oTaggerInput.OutputFileName;

                var option = Program.TagOptions;

                using (var document = DocX.Load(stFilename))
                {
                    var documentParagraphs = document.Paragraphs;
                    var nParaCount = documentParagraphs.Count;

                    #region var lstParCauHoi = danh sách các par câu hỏi;
                    var lstParCauHoi = new List<Paragraph>();
                    var idxEndOfCauHoiPar = -1;
                    for (int i = 0; i < nParaCount; i++)
                    {
                        if (worker.CancellationPending)
                            return;
                        var par = documentParagraphs[i];
                        var parContent = par.Text.Trim('\r', '\n', '\t', ' ').ToUpper();
                        worker.ReportProgress(0, string.Format("I/ Nhận diện câu hỏi/đáp án {0}/{1}.", i + 1, nParaCount));
                        if (parContent != PHAN_DAP_AN)
                        {
                            if (laBatDauCau(parContent) || laBatDauKhoi(parContent))
                                lstParCauHoi.Add(par);
                            //Debug.WriteLine("... là câu hỏi");
                        }
                        else
                        {
                            idxEndOfCauHoiPar = i;
                            //Debug.WriteLine("... là đáp án");
                            break;
                        }
                    }
                    #endregion

                    #region var lstParDapAn = danh sách các par đáp án; bổ sung [lanw_start] ở đầu phần đáp án
                    var lstParDapAn = new List<Paragraph>();
                    for (int i = idxEndOfCauHoiPar; i < nParaCount; i++)
                    {
                        if (worker.CancellationPending)
                            return;
                        //Debug.WriteLine("Bổ sung đoạn trả lời " + i);
                        worker.ReportProgress(0, string.Format("I/ Nhận diện câu hỏi/đáp án {0}/{1}.(*)", i + 1, nParaCount));
                        lstParDapAn.Add(documentParagraphs[i]);
                    }

                    // Loại bỏ các đoạn rỗng (nếu có) ở phần cuối cùng
                    var idxLast = lstParDapAn.Count - 1;
                    while (true)
                    {
                        var par = lstParDapAn[idxLast];
                        var parContent = par.Text.Trim(' ', '\t', '\n', '\r');
                        if (parContent == "")
                        {
                            lstParDapAn.RemoveAt(idxLast);
                            idxLast = lstParDapAn.Count - 1;
                            if (idxLast < 0)
                                break;
                        }
                        else
                            break;
                    }
                    #endregion

                    #region Bổ sung các thẻ cho các khối câu hỏi, câu hỏi
                    var nParCauHoi = lstParCauHoi.Count;
                    var laKhoiCauHoiDauTien = true;
                    var laCauHoiDauTienCuaKhoi = true;
                    for (int i = 0; i < lstParCauHoi.Count; i++)
                    {
                        if (worker.CancellationPending)
                            return;
                        worker.ReportProgress(0, string.Format("II/ Bổ sung thẻ cho câu hỏi {0}/{1}.", i + 1, nParCauHoi));
                        var par = lstParCauHoi[i];
                        var parContent = par.Text.TrimStart('\t', '\r', '\n', ' ').ToUpper();

                        var batDauKhoi = laBatDauKhoi(parContent);
                        if (batDauKhoi)
                        {
                            #region Bắt đầu 1 khối..
                            laCauHoiDauTienCuaKhoi = true;
                            if (laKhoiCauHoiDauTien)
                            {
                                #region là khối câu hỏi đầu tiên => [lq_start]\[part_start]
                                var textToInsert = option.PhanCauHoi_Begin + NEWLINE + option.KhoiCauHoi_Begin;
                                try
                                {
                                    Debug.WriteLine("Là khối câu hỏi đầu tiên: Insert " + textToInsert);
                                    par.InsertTextBeforeFirstVisibleChar(textToInsert);
                                }
                                catch (Exception exc)
                                {
                                    lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho khối câu hỏi đầu tiên [{0}]", par.Text), exc));
                                }
                                laKhoiCauHoiDauTien = false;
                                #endregion
                            }
                            else
                            {
                                #region là khối câu hỏi thứ 2, 3, ... => [question_end][part_end]\[part_start]
                                try
                                {
                                    par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.KhoiCauHoi_End + NEWLINE + option.KhoiCauHoi_Begin);
                                }
                                catch (Exception exc)
                                {
                                    lstExc.Add(new Exception(string.Format("Thêm thẻ cho khối câu hỏi [{0}]", par.Text), exc));
                                }
                                #endregion
                            }
                            #endregion
                        }
                        else
                        {
                            #region Bắt đầu 1 câu hỏi?
                            var batDauCauHoi = laBatDauCau(parContent);

                            if (batDauCauHoi)
                            {
                                if (laCauHoiDauTienCuaKhoi)
                                {
                                    #region câu hỏi đầu tiên của khối => [question_start]
                                    //Debug.WriteLine(string.Format("InsertText câu đầu tiên của khối " + parContent));
                                    try
                                    {
                                        par.InsertTextBeforeFirstVisibleChar(option.CauHoi_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi đầu tiên của khối [{0}]", par.Text), exc));
                                    }
                                    laCauHoiDauTienCuaKhoi = false;
                                    #endregion
                                }
                                else
                                {
                                    #region câu hỏi thứ 2, 3, ... của khối => [question_end]\[question_start]
                                    //Debug.WriteLine(string.Format("InsertText câu " + parContent));
                                    //par.InsertText(0, option.CauHoi_End + NEWLINE + option.CauHoi_Begin);
                                    try
                                    {
                                        par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.CauHoi_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi trong khối [{0}]", par.Text), exc));
                                    }
                                    #endregion
                                }
                            }
                            #endregion
                        }
                    }
                    #endregion

                    #region Đóng câu hỏi cuối cùng, đóng khối câu hỏi cuối cùng và đóng phần câu hỏi [question_end]\[part_end]\[lq_end]
                    var lastQuestionPar = lstParCauHoi[lstParCauHoi.Count - 1];
                    lastQuestionPar.AppendLine(option.CauHoi_End);
                    lastQuestionPar.AppendLine(option.KhoiCauHoi_End);
                    lastQuestionPar.AppendLine(option.PhanCauHoi_End);
                    #endregion

                    #region Bổ sung các thẻ cho các khối trả lời, câu trả lời
                    var laDapAnDauTienCuaKhoi = true;
                    var laKhoiDapAnDauTien = true;
                    int soLuongDapAn = 0;
                    var nParDapAn = lstParDapAn.Count;
                    for (int i = 0; i < lstParDapAn.Count; i++)
                    {
                        if (worker.CancellationPending)
                            return;
                        worker.ReportProgress(0, string.Format("III/Bổ sung thẻ cho đáp án {0}/{1}.", i + 1, nParDapAn));
                        var par = lstParDapAn[i];
                        var parContent = par.Text.Trim(' ', '\t', '\r', '\n').ToUpper();
                        if (parContent == PHAN_DAP_AN)
                        {
                            #region Bắt đầu phần đáp án => Bổ sung [lanw_start] truước II. PHẦN ĐÁP ÁN
                            //par.InsertText(0, option.PhanDapAn_Begin);
                            try
                            {
                                par.InsertTextBeforeFirstVisibleChar(option.PhanDapAn_Begin);
                            }
                            catch (Exception exc)
                            {
                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu phần đáp án cho [{0}]", par.Text), exc));
                            }
                            #endregion
                        }
                        else
                        {
                            var batDauKhoi = laBatDauKhoi(parContent); //parcontent.StartsWith("KHỐI ")
                            if (batDauKhoi)
                            {
                                laDapAnDauTienCuaKhoi = true;
                                #region Bắt đầu khối đáp án => bổ sung kết thúc đáp án ([anw_end]) cuối cùng của khối trước đó (nếu có)
                                if (laKhoiDapAnDauTien == false)
                                {
                                    var idxPrevAnswer = i - 3;
                                    if (idxPrevAnswer >= 0 && soLuongDapAn > 0)
                                    {
                                        var parPrevAnswer = lstParDapAn[idxPrevAnswer];
                                        try
                                        {
                                            parPrevAnswer.Append(option.DapAn_End);
                                        }
                                        catch (Exception exc)
                                        {
                                            lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc cho đáp án trước khối [{0}]", par.Text), exc));
                                        }
                                    }
                                }
                                laKhoiDapAnDauTien = false;
                                #endregion
                            }
                            else
                            {
                                var batDauCau = laBatDauCau(parContent); //parcontent.StartsWith(CAU)
                                if (batDauCau)
                                {
                                    #region Bắt đầu 1 đáp án cho câu hỏi => [anw_start]
                                    try
                                    {
                                        par.InsertTextBeforeFirstVisibleChar(option.DapAn_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu đáp án cho câu [{0}]", par.Text), exc));
                                    }
                                    #endregion

                                    soLuongDapAn++;

                                    if (laDapAnDauTienCuaKhoi == false)
                                    {
                                        #region Không phải là đáp án đầu tiên => Thêm phần kết thúc đáp án [anw_end] của câu trước đó
                                        var idxPrevParagraph = i - 1;
                                        if (0 <= idxPrevParagraph && idxPrevParagraph <= lstParDapAn.Count - 1)
                                        {
                                            try
                                            {
                                                var prevPara = lstParDapAn[idxPrevParagraph];
                                                prevPara.Append(option.DapAn_End);
                                            }
                                            catch (Exception exc)
                                            {
                                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc đáp án cho câu hỏi [{0}]", par.Text), exc));
                                            }
                                        }
                                        #endregion
                                    }
                                    laDapAnDauTienCuaKhoi = false;
                                }
                            }
                        }
                    }
                    #endregion

                    #region Đóng đáp án cuối cùng => [anw_end]
                    // Ô tương ứng điểm số của đáp án cuối là par cách par cuối cùng 2 phần tử
                    var idxLastAnswerPar = lstParDapAn.Count - 3;
                    if (0 <= idxLastAnswerPar && idxLastAnswerPar <= lstParDapAn.Count - 1)
                    {
                        var lastAnswerPar = lstParDapAn[idxLastAnswerPar];
                        lastAnswerPar.Append(option.DapAn_End);
                    }
                    #endregion

                    #region Đóng phần đáp [lanw_end]
                    document.Tables[document.Tables.Count - 1].InsertParagraphAfterSelf(option.PhanDapAn_End);
                    #endregion

                    worker.ReportProgress(0, "Ghi ra tệp");
                    document.SaveAs(stFilename2);
                }
                stopwatch.Stop();
                var totalErrors = new StringBuilder();
                for (int i = 0; i < lstExc.Count; i++)
                    totalErrors.AppendFormat("{0}:{1}\r\n", lstExc[i].Message, lstExc[i].ToString());
                oE.Result = new TaggerOutput(stopwatch.ElapsedMilliseconds, totalErrors.ToString());
                #endregion
            };
            bw.ProgressChanged += (oSender, oE) =>
            {
                #region MyRegion
                try { lblProgress.Text = oE.UserState.ToString(); }
                catch { }
                #endregion
            };
            bw.RunWorkerCompleted += (oSender, oE) =>
            {
                #region RunWorkerCompleted
                var worker = oSender as BackgroundWorker;
                btnCancel.Enabled = false;
                btnCancel.Tag = null;
                btnProcess.Enabled = true;
                if (oE.Cancelled || worker.CancellationPending)
                {
                    MessageBox.Show("Quá trình chuyển đổi bị bỏ qua bởi người dùng");
                }
                else if (oE.Error != null)
                {
                    MessageBox.Show("Xuất hiện lỗi " + oE.Error);
                }
                else
                {
                    var result = oE.Result as TaggerOutput;
                    var totalMS = Convert.ToInt32(result.TotalWorkingTime);
                    string stTotalMS = "";
                    if (totalMS < 1000)
                        stTotalMS = string.Format("{0}ms", totalMS);
                    else if (totalMS < 60 * 1000)
                        stTotalMS = string.Format("{0}s", totalMS / 1000);
                    else if (totalMS < 60 * 60 * 1000)
                    {
                        var totalS = totalMS / 1000;
                        var totalM = totalS / 60;
                        var s = totalM % 60;
                        stTotalMS = string.Format("{0}m {1}s", totalM, s);
                    }
                    else
                    {
                        var x = new TimeSpan(0, 0, 0, 0, totalMS);
                        stTotalMS = string.Format("{0}m {1}s", x.TotalMinutes, x.TotalSeconds);
                    }

                    var totalErrors = result.TotalErrors;
                    if (string.IsNullOrEmpty(totalErrors))
                        MessageBox.Show(string.Format("Đã chuyển đổi thành công trong {0}", stTotalMS));
                    else
                        MessageBox.Show(string.Format("Đã chuyển đổi xong trong {0}\nChi tiết lỗi: {1}", stTotalMS, totalErrors));

                    lblProgress.Text = "";
                }
                #endregion
            };

            //btnCancel.Tag = bw;
            //btnCancel.Enabled = true;
            //btnProcess.Enabled = false;
            var ti = new TaggerInput(txtDocumentFileName.Text.Trim(), txtOutputDocumentFileName.Text.Trim());
            //bw.RunWorkerAsync(ti);
            var to = DoTaggingDocument(ti, Program.TagOptions);
            if (to.Error != null)
            {
                MessageBox.Show("Xuất hiện lỗi " + to.Error.Message);
            }
            else
            {
                var result = to;
                var totalMS = Convert.ToInt32(result.TotalWorkingTime);
                string stTotalMS = "";
                if (totalMS < 1000)
                    stTotalMS = string.Format("{0}ms", totalMS);
                else if (totalMS < 60 * 1000)
                    stTotalMS = string.Format("{0}s", totalMS / 1000);
                else if (totalMS < 60 * 60 * 1000)
                {
                    var totalS = totalMS / 1000;
                    var totalM = totalS / 60;
                    var s = totalM % 60;
                    stTotalMS = string.Format("{0}m {1}s", totalM, s);
                }
                else
                {
                    var x = new TimeSpan(0, 0, 0, 0, totalMS);
                    stTotalMS = string.Format("{0}m {1}s", x.TotalMinutes, x.TotalSeconds);
                }

                var totalErrors = result.TotalErrors;
                if (string.IsNullOrEmpty(totalErrors))
                    MessageBox.Show(string.Format("Đã chuyển đổi thành công trong {0}", stTotalMS));
                else
                    MessageBox.Show(string.Format("Đã chuyển đổi xong trong {0}\nChi tiết lỗi: {1}", stTotalMS, totalErrors));

                lblProgress.Text = "";
            }
        }

        

        public static TaggerOutput DoTaggingDocument(TaggerInput oTaggerInput, TagOption option)
        {
            try
            {
                var lstExc = new List<Exception>();
                var stopwatch = new System.Diagnostics.Stopwatch();
                stopwatch.Start();
                var stFilename = oTaggerInput.InputFileName;
                if (System.IO.File.Exists(stFilename) == false)
                    throw new Exception("Tệp đầu vào không tồn tại");
                var stFilename2 = oTaggerInput.OutputFileName;

                using (var document = DocX.Load(stFilename))
                {
                    #region Xử lý tài liệu document
                    var documentParagraphs = document.Paragraphs;
                    var nParaCount = documentParagraphs.Count;

                    #region var lstParCauHoi = danh sách các par câu hỏi;
                    var lstParCauHoi = new List<Paragraph>();
                    var idxEndOfCauHoiPar = -1;
                    for (int i = 0; i < nParaCount; i++)
                    {
                        var par = documentParagraphs[i];
                        var parContent = par.Text.Trim('\r', '\n', '\t', ' ').ToUpper();
                        if (parContent != PHAN_DAP_AN)
                        {
                            if (laBatDauCau(parContent) || laBatDauKhoi(parContent))
                                lstParCauHoi.Add(par);
                        }
                        else
                        {
                            idxEndOfCauHoiPar = i;
                            break;
                        }
                    }
                    #endregion

                    #region var lstParDapAn = danh sách các par đáp án; bổ sung [lanw_start] ở đầu phần đáp án
                    var lstParDapAn = new List<Paragraph>();
                    for (int i = idxEndOfCauHoiPar; i < nParaCount; i++)
                        lstParDapAn.Add(documentParagraphs[i]);

                    // Loại bỏ các đoạn rỗng (nếu có) ở phần cuối cùng
                    var idxLast = lstParDapAn.Count - 1;
                    while (true)
                    {
                        var par = lstParDapAn[idxLast];
                        var parContent = par.Text.Trim(' ', '\t', '\n', '\r');
                        if (parContent == "")
                        {
                            lstParDapAn.RemoveAt(idxLast);
                            idxLast = lstParDapAn.Count - 1;
                            if (idxLast < 0)
                                break;
                        }
                        else
                            break;
                    }
                    #endregion

                    #region Bổ sung các thẻ cho các khối câu hỏi, câu hỏi
                    var nParCauHoi = lstParCauHoi.Count;
                    var laKhoiCauHoiDauTien = true;
                    var laCauHoiDauTienCuaKhoi = true;
                    for (int i = 0; i < lstParCauHoi.Count; i++)
                    {
                        var par = lstParCauHoi[i];
                        var parContent = par.Text.TrimStart('\t', '\r', '\n', ' ').ToUpper();

                        var batDauKhoi = laBatDauKhoi(parContent);
                        if (batDauKhoi)
                        {
                            #region Bắt đầu 1 khối..
                            laCauHoiDauTienCuaKhoi = true;
                            if (laKhoiCauHoiDauTien)
                            {
                                #region là khối câu hỏi đầu tiên => [lq_start]\[part_start]
                                var textToInsert = option.PhanCauHoi_Begin + NEWLINE + option.KhoiCauHoi_Begin;
                                try
                                {
                                    //Debug.WriteLine("Là khối câu hỏi đầu tiên: Insert " + textToInsert);
                                    par.InsertTextBeforeFirstVisibleChar(textToInsert);
                                }
                                catch (Exception exc)
                                {
                                    lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho khối câu hỏi đầu tiên [{0}]", par.Text), exc));
                                }
                                laKhoiCauHoiDauTien = false;
                                #endregion
                            }
                            else
                            {
                                #region là khối câu hỏi thứ 2, 3, ... => [question_end][part_end]\[part_start]
                                try
                                {
                                    par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.KhoiCauHoi_End + NEWLINE + option.KhoiCauHoi_Begin);
                                }
                                catch (Exception exc)
                                {
                                    lstExc.Add(new Exception(string.Format("Thêm thẻ cho khối câu hỏi [{0}]", par.Text), exc));
                                }
                                #endregion
                            }
                            #endregion
                        }
                        else
                        {
                            #region Bắt đầu 1 câu hỏi?
                            var batDauCauHoi = laBatDauCau(parContent);

                            if (batDauCauHoi)
                            {
                                if (laCauHoiDauTienCuaKhoi)
                                {
                                    #region câu hỏi đầu tiên của khối => [question_start]
                                    //Debug.WriteLine(string.Format("InsertText câu đầu tiên của khối " + parContent));
                                    try
                                    {
                                        par.InsertTextBeforeFirstVisibleChar(option.CauHoi_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi đầu tiên của khối [{0}]", par.Text), exc));
                                    }
                                    laCauHoiDauTienCuaKhoi = false;
                                    #endregion
                                }
                                else
                                {
                                    #region câu hỏi thứ 2, 3, ... của khối => [question_end]\[question_start]
                                    //Debug.WriteLine(string.Format("InsertText câu " + parContent));
                                    //par.InsertText(0, option.CauHoi_End + NEWLINE + option.CauHoi_Begin);
                                    try
                                    {
                                        par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.CauHoi_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi trong khối [{0}]", par.Text), exc));
                                    }
                                    #endregion
                                }
                            }
                            #endregion
                        }
                    }
                    #endregion

                    #region Đóng câu hỏi cuối cùng, đóng khối câu hỏi cuối cùng và đóng phần câu hỏi [question_end]\[part_end]\[lq_end]
                    var lastQuestionPar = lstParCauHoi[lstParCauHoi.Count - 1];
                    lastQuestionPar.AppendLine(option.CauHoi_End);
                    lastQuestionPar.AppendLine(option.KhoiCauHoi_End);
                    lastQuestionPar.AppendLine(option.PhanCauHoi_End);
                    #endregion

                    #region Bổ sung các thẻ cho các khối trả lời, câu trả lời
                    var laDapAnDauTienCuaKhoi = true;
                    var laKhoiDapAnDauTien = true;
                    int soLuongDapAn = 0;
                    var nParDapAn = lstParDapAn.Count;
                    for (int i = 0; i < lstParDapAn.Count; i++)
                    {
                        var par = lstParDapAn[i];
                        var parContent = par.Text.Trim(' ', '\t', '\r', '\n').ToUpper();
                        if (parContent == PHAN_DAP_AN)
                        {
                            #region Bắt đầu phần đáp án => Bổ sung [lanw_start] truước II. PHẦN ĐÁP ÁN
                            try
                            {
                                par.InsertTextBeforeFirstVisibleChar(option.PhanDapAn_Begin);
                            }
                            catch (Exception exc)
                            {
                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu phần đáp án cho [{0}]", par.Text), exc));
                            }
                            #endregion
                        }
                        else
                        {
                            if (laBatDauKhoi(parContent))
                            {
                                laDapAnDauTienCuaKhoi = true;
                                #region Bắt đầu khối đáp án => bổ sung kết thúc đáp án ([anw_end]) cuối cùng của khối trước đó (nếu có)
                                if (laKhoiDapAnDauTien == false)
                                {
                                    var idxPrevAnswer = i - 3;
                                    if (idxPrevAnswer >= 0 && soLuongDapAn > 0)
                                    {
                                        var parPrevAnswer = lstParDapAn[idxPrevAnswer];
                                        try
                                        {
                                            parPrevAnswer.Append(option.DapAn_End);
                                        }
                                        catch (Exception exc)
                                        {
                                            lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc cho đáp án trước khối [{0}]", par.Text), exc));
                                        }
                                    }
                                }
                                laKhoiDapAnDauTien = false;
                                #endregion
                            }
                            else if (laBatDauCau(parContent))
                            {
                                #region là bắt đầu câu
                                #region Bắt đầu 1 đáp án cho câu hỏi => [anw_start]
                                try
                                {
                                    par.InsertTextBeforeFirstVisibleChar(option.DapAn_Begin);
                                }
                                catch (Exception exc)
                                {
                                    lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu đáp án cho câu [{0}]", par.Text), exc));
                                }
                                #endregion

                                soLuongDapAn++;

                                if (laDapAnDauTienCuaKhoi == false)
                                {
                                    #region Không phải là đáp án đầu tiên => Thêm phần kết thúc đáp án [anw_end] của câu trước đó
                                    var idxPrevParagraph = i - 1;
                                    if (0 <= idxPrevParagraph && idxPrevParagraph <= lstParDapAn.Count - 1)
                                    {
                                        try
                                        {
                                            var prevPara = lstParDapAn[idxPrevParagraph];
                                            prevPara.Append(option.DapAn_End);
                                        }
                                        catch (Exception exc)
                                        {
                                            lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc đáp án cho câu hỏi [{0}]", par.Text), exc));
                                        }
                                    }
                                    #endregion
                                }
                                laDapAnDauTienCuaKhoi = false;
                                #endregion
                            }
                        }
                    }
                    #endregion

                    #region Đóng đáp án cuối cùng => [anw_end]
                    // Ô tương ứng điểm số của đáp án cuối là par cách par cuối cùng 2 phần tử
                    var idxLastAnswerPar = lstParDapAn.Count - 3;
                    if (0 <= idxLastAnswerPar && idxLastAnswerPar <= lstParDapAn.Count - 1)
                    {
                        var lastAnswerPar = lstParDapAn[idxLastAnswerPar];
                        lastAnswerPar.Append(option.DapAn_End);
                    }
                    #endregion

                    #region Đóng phần đáp [lanw_end]
                    document.Tables[document.Tables.Count - 1].InsertParagraphAfterSelf(option.PhanDapAn_End);
                    #endregion

                    document.SaveAs(stFilename2);
                    #endregion
                }
                stopwatch.Stop();
                var totalErrors = new StringBuilder();
                for (int i = 0; i < lstExc.Count; i++)
                    totalErrors.AppendFormat("{0}:{1}\r\n", lstExc[i].Message, lstExc[i].ToString());
                return new TaggerOutput(stopwatch.ElapsedMilliseconds, totalErrors.ToString());
            }
            catch (Exception exc)
            {
                return new TaggerOutput(exc);
            }
        }

        void bindingSource1_CurrentItemChanged(object sender, EventArgs e)
        {
            if (bindingSource1.Current != null)
                richTextBox1.Text = bindingSource1.Current.ToString();
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            lblProgress.Text = "";
            btnCancel.Enabled = false;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (btnCancel.Tag != null)
            {
                btnCancel.Enabled = false;
                (btnCancel.Tag as BackgroundWorker).CancelAsync();
            }
        }

        private void bindingSource1_CurrentChanged(object sender, EventArgs e)
        {
            try
            {
                var x = bindingSource1.Current as Paragraph;
                richTextBox1.Text = x.Text;
            }
            catch (Exception exc)
            {
                richTextBox1.Text = "Lỗi " + exc;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var stText = "		Câu 1.1 (2 điểm):";
            var batDauCau = laBatDauCau(stText.TrimStart(' '));
            MessageBox.Show(batDauCau.ToString());
        }
    }
}
